package com.ydy.service.wechat.impl;

import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSONObject;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.ydy.configuration.WeChatConfig;
import com.ydy.constant.OrderStatusEnum;
import com.ydy.exception.BusinessException;
import com.ydy.exception.RemoteApiException;
import com.ydy.ienum.EnumOrder;
import com.ydy.mapper.RefundMapper;
import com.ydy.model.Book;
import com.ydy.model.Order;
import com.ydy.model.Refund;
import com.ydy.model.Staff;
import com.ydy.model.TimeQuantum;
import com.ydy.model.User;
import com.ydy.remote.wechat.WeChatApi;
import com.ydy.remote.wechat.ienum.MsgType;
import com.ydy.remote.wechat.vo.custom.Image;
import com.ydy.remote.wechat.vo.custom.ImageMsg;
import com.ydy.remote.wechat.vo.menu.Menu;
import com.ydy.remote.wechat.vo.message.BaseMessage;
import com.ydy.remote.wechat.vo.message.EventMessage;
import com.ydy.remote.wechat.vo.message.TextMessage;
import com.ydy.service.book.BookService;
import com.ydy.service.order.OrderService;
import com.ydy.service.timeQuantum.TimeQuantumService;
import com.ydy.service.wechat.WechatService;
import com.ydy.utils.AesUtil;
import com.ydy.utils.BigDecimalUtil;
import com.ydy.utils.DateUtil;
import com.ydy.utils.Md5Util;
import com.ydy.utils.MoneyUtil;
import com.ydy.utils.XmlUtil;
import com.ydy.vo.other.BaseVo;
import com.ydy.vo.other.ResultVo;

@Service
@Transactional
public class WechatServiceImpl implements WechatService {
	private final static Logger log = LoggerFactory.getLogger(WechatServiceImpl.class);

	private String SUB_TEXT = "一家以YOUNG态度与EASY生活为理念的照相馆，在追求美的路上，我们与您一路相伴。\n" + " \n" + "*点击菜单栏【EASY 照相馆】立即预约，\n" + " \n"
			+ "*点击菜单栏【EASY WEDDING】了解婚礼详情。\n" + " \n" + "如您需要帮助，请拔打客服联系电话：【18520663995】或在本对话框直接输入文字开始对话\n" + " \n"
			+ "客服在线时间：10：00-22：00，\n" + "点击二维码可以添加我们小EASY咨询更多详情哦！";
	@Autowired
	private WeChatConfig wxConfig;

	@Autowired
	private OrderService orderService;
	@Autowired
	private BookService bookService;
	@Autowired
	private TimeQuantumService timeQuantumService;
	@Autowired
	private RefundMapper refundMapper;

	@Override
	public BaseMessage handleMessage(String xml) {
		BaseMessage message = XmlUtil.xmlToObject(xml, BaseMessage.class);
		if (MsgType.text.toString().equalsIgnoreCase(message.getMsgType())) {
			TextMessage textMessage = XmlUtil.xmlToObject(xml, TextMessage.class);
			TextMessage toMessage = new TextMessage();
			toMessage.setToUserName(textMessage.getFromUserName());
			toMessage.setFromUserName(textMessage.getToUserName());
			toMessage.setContent(SUB_TEXT);
			message = toMessage;
			Image image = new Image();
			image.setMediaId("7sNfzNNZrYOm1_FU7NraN58ep0j7rpRc4ks0RqjlsWd5WZ83E4m4k32E4NjCd9ce");
			ImageMsg msg = new ImageMsg();
			msg.setImage(image);
			msg.setTouser(toMessage.getToUserName());
			WeChatApi.customSend(msg);
		} else if (MsgType.event.toString().equalsIgnoreCase(message.getMsgType())) {
			EventMessage eventMessage = XmlUtil.xmlToObject(xml, EventMessage.class);
			if (eventMessage.getEvent().equalsIgnoreCase("subscribe")) {
				TextMessage toMessage = new TextMessage();
				toMessage.setToUserName(eventMessage.getFromUserName());
				toMessage.setFromUserName(eventMessage.getToUserName());
				toMessage.setContent(SUB_TEXT);
				message = toMessage;
				Image image = new Image();
				image.setMediaId("7sNfzNNZrYOm1_FU7NraN58ep0j7rpRc4ks0RqjlsWd5WZ83E4m4k32E4NjCd9ce");
				ImageMsg msg = new ImageMsg();
				msg.setImage(image);
				msg.setTouser(toMessage.getToUserName());
				WeChatApi.customSend(msg);
			}
		}
		return message;
	}

	@Override
	public BaseVo createMenu(Menu menu) {
		log.info("菜单json:{}", JSONObject.toJSON(menu));
		WeChatApi.createMenu(menu);
		return new ResultVo();
	}

	@Override
	public Object unifiedOrder(Long orderId, User user, String remoteIp) {
		Order vo = orderService.selectOrderAndStatusByIdAndUser(orderId, user);
		if (!OrderStatusEnum.COMMIT.getCode().equals(vo.getStatus())) {
			throw new BusinessException(EnumOrder.ORDER_STATUS_NOT_COMMIT);
		}
		try {
			WXPay wxpay = new WXPay(wxConfig);
			Map<String, String> data = new HashMap<>();
			// 生成商户订单号，不可重复
			data.put("openid", user.getOpenId());
			data.put("body", vo.getName());
			data.put("out_trade_no", orderId + "");
			data.put("total_fee",
					BigDecimalUtil.mul(new BigDecimal(vo.getActualPay()), new BigDecimal(100)).longValue() + "");
			// 自己的服务器IP地址
			data.put("spbill_create_ip", remoteIp);
			// 异步通知地址（请注意必须是外网）
			data.put("notify_url", wxConfig.getWechatPayNotifyUrl());
			// 交易类型
			data.put("trade_type", wxConfig.getTradeType());
			// 附加数据，在查询API和支付通知中原样返回，该字段主要用于商户携带订单的自定义数据
			data.put("attach", "");
			// 使用官方API请求预付订单
			Map<String, String> response = wxpay.unifiedOrder(data);
			if ("SUCCESS".equals(response.get("return_code"))) {// 主要返回以下5个参数
				Map<String, String> param = new HashMap<>();
				param.put("appId", wxConfig.getAppId());
				param.put("timeStamp", System.currentTimeMillis() / 1000 + "");
				param.put("nonceStr", WXPayUtil.generateNonceStr());
				param.put("package", "prepay_id=" + response.get("prepay_id"));
				param.put("signType", WXPayConstants.SignType.MD5.toString());
				param.put("paySign",
						WXPayUtil.generateSignature(param, wxConfig.getKey(), WXPayConstants.SignType.MD5));
				return param;
			} else {
				throw new RemoteApiException(response.get("return_msg"));
			}
		} catch (Exception e) {
			throw new RemoteApiException(e.getMessage());
		}
	}

	@Override
	public Object notify(String xml) {
		String xmlBack = WeChatApi.NOTIFY_FAIL;
		WXPay wxpay = new WXPay(wxConfig);
		Map<String, String> notifyMap = null;
		try {
			notifyMap = WXPayUtil.xmlToMap(xml); // 调用官方SDK转换成map类型数据
			if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {// 验证签名是否有效，有效则进一步处理
				String return_code = notifyMap.get("return_code");// 状态
				String out_trade_no = notifyMap.get("out_trade_no");// 商户订单号
				if (return_code.equals("SUCCESS")) {
					if (StringUtils.isEmpty(out_trade_no)) {
						log.error("微信手机支付回调失败订单号为空");
					} else {
						// 注意特殊情况：订单已经退款，但收到了支付结果成功的通知，不应把商户的订单状态从退款改成支付成功
						// 注意特殊情况：微信服务端同样的通知可能会多次发送给商户系统，所以数据持久化之前需要检查是否已经处理过了，处理了直接返回成功标志
						// 业务数据持久化
						log.info("微信手机支付回调成功订单号:{}", out_trade_no);
						Long orderId = Long.parseLong(out_trade_no);
						Order order = orderService.selectOrderAndStatusByIdAndUser(orderId, null);
						if (!OrderStatusEnum.COMMIT.getCode().equals(order.getStatus())) {// 如果不是未付款状态直接返回成功
							xmlBack = WeChatApi.NOTIFY_SUS;
							return xmlBack;
						}
						Long totalFee = Long.parseLong(notifyMap.get("total_fee"));
						Long payTotal = BigDecimalUtil.mul(new BigDecimal(order.getActualPay()), new BigDecimal(100))
								.longValue();
						if (totalFee < payTotal)// 支付费用大于等于订单费用才行
						{
							log.error("订单金额(分)={},支付金额(分)={}", payTotal, totalFee);
							throw new BusinessException(EnumOrder.FEE_NOT_FULL);
						}
						String time = notifyMap.get("time_end") + "000";// 20141030133525 微信传回时间
						orderService.updateOrderStatusPay(orderId, DateUtil.parseDate(time));
						xmlBack = WeChatApi.NOTIFY_SUS;
						return xmlBack;
					}
				}
			} else {
				// 签名错误，如果数据里没有sign字段，也认为是签名错误
				// 失败的数据要不要存储？
				log.error("支付回调通知签名错误:({}){}", notifyMap.get("err_code"), notifyMap.get("err_code_des"));
			}
		} catch (Exception e) {
			log.error("手机支付回调通知失败:{}", e.getMessage());
			log.error("微信回调xml:{}" + xml);
		}
		return xmlBack;
	}

	@Override
	public Object refund(Long orderId, String remark, User user, Staff staff) {
		Order vo = orderService.selectOrderAndStatusByIdAndUser(orderId, user);
		if (!OrderStatusEnum.PAY.getCode().equals(vo.getStatus())) {
			throw new BusinessException(EnumOrder.ORDER_STATUS_NOT_PAY);
		}
		Book book = bookService.selectById(orderId);
		TimeQuantum time = timeQuantumService.selectById(book.getTimeQuantumId());
		Date bookTime = DateUtil.createTime(book.getBookTime(), time.getStartTimeHour(), time.getStartTimeMinute(), 0);
		Date now = new Date();
		Long betweenTime = bookTime.getTime() - now.getTime();
		Integer percent = 100;
		if (user != null) {
			if (betweenTime > (3 * 1000 * 3600) && betweenTime < (24 * 3600 * 1000)) {
				percent = 50;
			} else if (betweenTime >= (24 * 3600 * 1000)) {
				percent = 100;
			} else {
				throw new BusinessException(EnumOrder.REFUND_EXPIRE);
			}
		} else if (staff != null) {
			if (!Objects.equals(staff.getShopId(), vo.getShopId())) {
				throw new BusinessException(EnumOrder.ORDER_SHOP_NOT_FIT);
			}
			percent = 100;// 商家操作退全款
		} else {
			throw new BusinessException(EnumOrder.ROLE_REFUND_NOT_FOUND);
		}
		try {
			WXPay wxpay = new WXPay(wxConfig);
			Map<String, String> data = new HashMap<>();
			// 生成商户订单号，不可重复
			data.put("out_trade_no", orderId + "");
			data.put("out_refund_no", System.currentTimeMillis() + "");
			Long total = MoneyUtil.yuanToCent(vo.getActualPay());
			data.put("total_fee", total + "");
			Long refundTotal = BigDecimalUtil.discount(new BigDecimal(total), new BigDecimal(percent)).longValue();
			data.put("refund_fee", refundTotal + "");
			if (StringUtils.isEmpty(remark)) {
				remark = "nothing";
			}
			data.put("refund_desc", remark);
			// 异步通知地址（请注意必须是外网）
			// data.put("notify_url", wxConfig.getWechatRefundPayNotifyUrl());//暂时不用
			// 使用官方API请求预付订单
			Map<String, String> response;
			try {
				response = wxpay.refund(data);
			} catch (Exception e) {
				throw new RemoteApiException(e.getMessage());
			}
			log.info("退款返回:" + JSONObject.toJSONString(response));
			if ("SUCCESS".equals(response.get("return_code"))) {// 主要返回以下5个参数
				Refund refund = new Refund();
				refund.setId(orderId);
				refund.setOrderId(orderId);
				refund.setShopId(vo.getShopId());
				refund.setUserId(user.getId());
				refund.setCreateTime(now);
				refund.setRefundTime(now);
				refund.setActualPay(MoneyUtil.centToYuan(total));
				refund.setRefundPay(MoneyUtil.centToYuan(refundTotal));
				refundMapper.insertSelective(refund);
				orderService.updateOrderStatusRefund(orderId, now);
				return new ResultVo();
			} else {
				throw new RemoteApiException(response.get("return_msg"));
			}
		} catch (RuntimeException e) {
			throw e;
		}
	}

	@Override
	public Object notifyRefund(String xml) {
		String xmlBack = WeChatApi.NOTIFY_FAIL;
		Map<String, String> notifyMap = null;
		try {
			notifyMap = WXPayUtil.xmlToMap(xml); // 调用官方SDK转换成map类型数据
			String return_code = notifyMap.get("return_code");// 状态
			String req_info = notifyMap.get("req_info");// 状态
			if (return_code.equals("SUCCESS")) {
				// 注意特殊情况：订单已经退款，但收到了支付结果成功的通知，不应把商户的订单状态从退款改成支付成功
				// 注意特殊情况：微信服务端同样的通知可能会多次发送给商户系统，所以数据持久化之前需要检查是否已经处理过了，处理了直接返回成功标志
				// 业务数据持久化
				if (!StringUtils.isEmpty(req_info)) {
					byte[] codes = Base64.getDecoder().decode(req_info);
					req_info = new String(codes, Charset.forName("utf-8"));
					String key = Md5Util.getMD5(wxConfig.getWxpayKey()).toLowerCase();
					req_info = AesUtil.decryptData(req_info, key);
					notifyMap = WXPayUtil.xmlToMap(req_info);
					String refund_status = notifyMap.get("refund_status");
					if ("SUCCESS".equals(refund_status)) {
						Long orderId = Long.parseLong(notifyMap.get("out_trade_no"));
						Long out_refund_no = Long.parseLong(notifyMap.get("out_refund_no"));
						Long total_fee = Long.parseLong(notifyMap.get("total_fee"));
						Long refund_fee = Long.parseLong(notifyMap.get("refund_fee"));
						Order order = orderService.selectOrderAndStatusByIdAndUser(orderId, null);
						if (OrderStatusEnum.REFUND.getCode().equals(order.getStatus())) {// 如果不是未付款状态直接返回成功
							xmlBack = WeChatApi.NOTIFY_SUS;
							return xmlBack;
						}
						if (OrderStatusEnum.REFUNDING.getCode().equals(order.getStatus())) {
							Refund refund = new Refund();
							refund.setId(out_refund_no);
							refund.setOrderId(order.getOrderId());
							refund.setShopId(order.getShopId());
							refund.setUserId(order.getUserId());
							Date now = new Date();
							refund.setCreateTime(now);
							refund.setRefundTime(now);
							refund.setActualPay(MoneyUtil.centToYuan(total_fee));
							refund.setRefundPay(MoneyUtil.centToYuan(refund_fee));
							refundMapper.insertSelective(refund);
							orderService.updateOrderStatusRefund(orderId, now);
							xmlBack = WeChatApi.NOTIFY_SUS;
							return xmlBack;
						}
					} else {
						log.error("退款状态为:" + refund_status);
					}
					return xmlBack;
				} else {
					log.error("req_info为空");
				}
			}
		} catch (Exception e) {
			log.error("手机支付回调通知失败:{}", e.getMessage());
			log.error("微信回调xml:{}" + xml);
		}
		return xmlBack;
	}
}
